#ifndef __TCPSERVER_HPP__
#define __TCPSERVER_HPP__

#include <functional>
#include <unordered_map>
#include <ctime>
#include "MySock.hpp"
#include "log.hpp"
#include "Epoll.hpp"
#include "Protocal.hpp"

namespace QiHai
{
    // 用户管理
    class User
    {
    public:
        std::string _ip;
        std::uint16_t _port;

        User(std::string ip, uint16_t port)
        :_ip(ip), _port(port)
        {}
    };

    class TcpServer;
    class Connection;
    using func_t = std::function<void(Connection*)>;  // 注意前面需要声明哦
    using service_logic = std::function<void(Connection*, std::string)>;
    static const time_t MAX_TIME = 20;  // 最大时间间隔
    // 每个sock对应一个连接类进行管理，里面存在其输入输出缓冲区，以及对应读取、写入、异常的回调方法
    class Connection
    {
    public:
        int _sock;

        // 三个回调函数
        func_t _recv_cb;
        func_t _send_cb;
        func_t _except_cb;

        // 接收缓冲却和发送缓冲区
        std::string _inbuffer;  // 当前存在bug，不能处理二进制流，但是文本是可以的
        std::string _outbuffer;

        TcpServer* _tsvr;  // 对服务器的一个回调指针，后续会使用到

    public:
        time_t _time;  // 当前的时间戳

        Connection(int sock, TcpServer* tsvr)
        :_sock(sock), _tsvr(tsvr)
        {}

        // 设置三种回调函数
        void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb)
        {
            _recv_cb = recv_cb;
            _send_cb = send_cb;
            _except_cb = except_cb;
        }

        ~Connection()
        {}
    };

    class TcpServer
    {
        const static uint16_t gport = 8080;
        const static int gnum = 128;
    private:
        int _listensock;
        uint16_t _port;
        Epoll _poll;
        std::unordered_map<int, Connection*> _connections;  // 组织链接
        std::unordered_map<int, User*> _users;  // 组织用户
        struct epoll_event* _revs;  // 读取就绪事件的缓冲区
        int _revs_num;  // 每次读就绪事件的大小，这里初始化的时候固定大小即可
        service_logic _call;  // 处理业务逻辑的函数，由上层提供
    private:
        // 将此sock建立一个连接对象描述起来，并且连接到map。设置三个回调函数。默认关心的事件是读取，写入和异常后续设置
        // 任何多路转接的服务器，一般默认只会打开对读取事件的关心，写入事件按需打开 
        void AddConnection(int sock, func_t recv_cb, func_t send_cb, func_t except_cb)
        {
            if(!QiHai::Sock::setNonBlock(sock)) exit(2);  // 设置其属性为非阻塞
            Connection* connection = new Connection(sock, this);
            connection->_time = time(nullptr);  // 初始时间戳
            connection->SetCallBack(recv_cb, send_cb, except_cb);
            _connections.insert(std::make_pair(sock, connection));
            // 添加入epoll模型中，默认关系读取事件
            if (!_poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET)) exit(6);  // 主动设置为ET模式对其进行监听
        }

        // 专门处理监听套接字的读取回调函数
        void Accepter(Connection* conn)
        {
            logMessage(DEBUG, "Accepter wait......");
            std::string client_ip;
            uint16_t client_port;
            int err_num = 0;
            while(true)
            {
                int sock = QiHai::Sock::accept(conn->_sock, &err_num, &client_ip, &client_port);
                // 注意，sock设置的是非阻塞读取，所以循环的进行绑定，一次性读完
                if (sock < 0)
                {
                    if (err_num == EAGAIN || err_num == EWOULDBLOCK) break;  // 此时只是没有链接了，读完了
                    else if(err_num == EINTR) continue;  // 此时是被中断了
                    else{
                        logMessage(WARNING, "accept error...... %d:%s", err_num, strerror(err_num));
                        break;  // 此时错误了，但是影响不大，break即可
                    }
                }
                _users.insert(std::make_pair(sock, new User(client_ip, client_port)));
                AddConnection(sock, std::bind(&TcpServer::Recver, this, std::placeholders::_1), \
                                    std::bind(&TcpServer::Sender, this, std::placeholders::_1), \
                                    std::bind(&TcpServer::Excpeter, this, std::placeholders::_1));

                logMessage(DEBUG, "is %d connection! add Connection success!", sock);
            }
        }

        // 三大回调方法
        // 读回调
        void Recver(Connection* conn)
        {
            // 此链接读的事件响应，处理读的事件
            // 首先保证将本次从底层的读取缓冲区拿完到用户的缓冲区
            conn->_time = time(nullptr);  // 更新时间戳
            bool err = false;
            const int nums = 1024;
            while(true)
            {
                char buffer[nums];
                int n = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);  // 此时最后一个0没有多大意义。因为sock始终保证的就是非阻塞进行读取
                if(n > 0)
                {
                    // 读取成功
                    buffer[n] = '\0';
                    conn->_inbuffer += buffer;
                }
                else if(n == 0)
                {
                    // 对方关闭连接
                    logMessage(DEBUG, "sock-%d[%s:%d] close connection, server close connection!", \
                                       conn->_sock, _users[conn->_sock]->_ip.c_str(), _users[conn->_sock]->_port);
                    conn->_except_cb(conn);  // 调用异常处理方法
                    err = true;
                    break;
                }
                else
                {
                    // 小于0判断是否读取完毕
                    if (errno == EAGAIN || errno == EWOULDBLOCK) break;  // 读取完毕正常退出
                    else if (errno == EINTR) continue;  // 中断了，在继续读取
                    else
                    {
                        err = true;
                        // 此时真的出错了
                        logMessage(FATAL, "recv error %d:%s", errno, strerror(errno));
                        conn->_except_cb(conn);  // 调用异常处理方法
                        break;
                    }
                }
            }
            if (!err)
            {
                logMessage(DEBUG, "sock:%d[%s:%d]# %s", \
                           conn->_sock, _users[conn->_sock]->_ip.c_str(), _users[conn->_sock]->_port, conn->_inbuffer.c_str());
                // 此时需要保证读取的数据是一个一个独立完整的报文，然后传递给上层逻辑进行处理
                std::vector<std::string> messages;
                QiHai::SpliteMessage(conn->_inbuffer, messages);  // test:{"x":1,"y":2,"op":43}{"x":2,"y":3,"op":47}{"x":0,"y":0,"op":37}{"x":1,
                for (auto& msg : messages)
                {
                    // 调用上层逻辑进行处理，服务器只是帮你提取出报文出来
                    _call(conn, msg);
                }
            }
        }

        void Sender(Connection* conn)
        {
            // 写也是非阻塞的写，也就是说，如果写入缓冲区满了也是会直接返回
            while(true)
            {
                int n = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                if (n > 0)
                {
                    // 写入成功
                    conn->_outbuffer.erase(0, n);
                    if (conn->_outbuffer.empty()) break;  // 写完了，退出
                }
                else{
                    if (errno == EAGAIN || errno == EWOULDBLOCK) break;  //  此时底层的发送缓冲区可能满了，不可发送，等下次机会 - 只要空了就会在执行这里
                    else if (errno == EINTR) continue;  // IO中断了，重新试一次
                    else{
                        // 此处出错了
                        logMessage(FATAL, "send error %d:%s", errno, strerror(errno));
                        conn->_except_cb(conn);
                        break;
                    }
                }
            }

            // 注意，如果将缓冲区内的数据发完了，需要关闭epoll对其写入事件的关心 - 因为一旦发送缓冲区变化了-变空 会通知的
            if (conn->_outbuffer.empty()) EnableReadWrite(conn, true, false);
            else EnableReadWrite(conn, true, true);
                // 保险起见，没发完的下次还要继续发
        }

        void Excpeter(Connection* conn)
        {
            User* user = _users[conn->_sock];
            _users.erase(conn->_sock);
            delete user;

            if (!_poll.DelFromEpoll(conn->_sock)) exit(6);  // 从epoll 模型中移除
            _connections.erase(conn->_sock);
            close(conn->_sock);  // 关闭此文件描述符
            delete conn;
        }

        bool IsConnectionExists(int sock)
        {
            auto res = _connections.find(sock);  // 可能后续因为异常，存在没有映射到此管理的表中的，这里需要检测一下
            return res != _connections.end();
        }
        
        void LoopOnce()
        {
            // 具体去就epoll就绪队列中获取
            int n = _poll.WaitEpoll(_revs, _revs_num);  // 默认10s阻塞一次
            if (n == 0)
            {
                logMessage(DEBUG, "events wait......");
            }
            else if (n < 0)
            {
                logMessage(FATAL, "epoll_wait error %d:%s", errno, strerror(errno));
                exit(7);
            }
            else{
                logMessage(DEBUG, "have events!");
                for (int i = 0; i < n; ++i)
                {
                    int sock = _revs[i].data.fd;
                    int event = _revs[i].events;

                    if (event & EPOLLERR) _revs[i].events |= (EPOLLIN | EPOLLOUT);  // 直接掉用recv、send报错后调用异常处理
                    if (event & EPOLLHUP) _revs[i].events |= (EPOLLIN | EPOLLOUT);  // 对方断开连接同理

                    if (event & EPOLLIN)
                    {
                        if(IsConnectionExists(sock) && _connections[sock]->_recv_cb)  // 首先是否找得到，然后判断是否注册了读取回调函数
                            _connections[sock]->_recv_cb(_connections[sock]);
                    }
                    if (event & EPOLLOUT)
                    {
                        if (IsConnectionExists(sock) && _connections[sock]->_send_cb)
                            _connections[sock]->_send_cb(_connections[sock]);
                    }
                }
            }
        }

        void Survivallink()  // 生存链接时间，发现大于MAX_TIME,主动断开连接
        {
            auto res = _connections.begin();
            while (res != _connections.end())
            {
                time_t t = time(nullptr);
                Connection* conn = res->second;
                ++res;
                if (t - conn->_time >= MAX_TIME && conn->_except_cb)  // 排除了sock
                {
                    logMessage(DEBUG, "sock:%d[%s:%d] No communication for a long time,close sock!",\
                                       conn->_sock, _users[conn->_sock]->_ip.c_str(), _users[conn->_sock]->_port);
                    conn->_except_cb(conn);
                }
            }
        }
    public:
        TcpServer(uint16_t port = gport)
        :_port(port), _revs(nullptr), _revs_num(gnum)
        {
            _listensock = QiHai::Sock::socket();
            if (_listensock < 0) exit(1);

            // 绑定
            if (!QiHai::Sock::bind(_listensock, _port)) exit(3);

            // 设置监听状态
            if (!QiHai::Sock::listen(_listensock)) exit(4);
            logMessage(DEBUG, "_listensock init success!");

            // 创建epoll模型
            if (!_poll.EpollCreate()) exit(5);
            logMessage(DEBUG, "epoll init success!");

            // 此时能否向以前那样，所有sock共享一个缓冲区呢(即裸的sock)？自然不能，可能存在多个sock是阶段性发送的，报文数据不完全。
            // 所以我们需要维护sock的每一个缓冲区，先描述在组织 使用类connection进行描述,使用哈希表实现的map进行一个组织
            // 在添加监听套接字对象到epoll之前，首先维护好一个就绪缓冲区
            _revs = new epoll_event[_revs_num];

            // 正式添加，我们增加一个接口，后续sock添加到里面都通过此接口 - 要建立连接类之类的，解耦一下
            AddConnection(_listensock, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
            logMessage(DEBUG, "listensock connection add success!");
        }

        // 修改连接在epoll模型的读写状态
        void EnableReadWrite(Connection* conn, bool readable, bool writeable)
        {
            int event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);
            if (!_poll.CtlSockToEpoll(conn->_sock, event)) exit(6);
        }

        // 服务器开始对特定事件等待并且进行一个派发
        void Dispather(service_logic func)
        {
            _call = func;  // 业务处理函数
            while(true)
            {
                Survivallink();  // 每次循环检查一次
                LoopOnce();  // 一次
            }
        }

        ~TcpServer()
        {
            close(_listensock);
            if (_revs != nullptr) delete[] _revs;
        }
    };


}

#endif